#include <cassert>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include "connection.hh"
#include "epoll_helper.hh"
#include "tcp_socket.hh"
#include "protocol.hh"
#include "../util.hh"
// 半同步半异步
class reactor_server_t
{
    friend connection_t;
    // function to handle requests
    using handler_t = std::function<bool(connection_t *)>;

public:
    reactor_server_t(handler_t req_handler, in_port_t port, int timeout_sec = 5)
        : _req_handler(req_handler),
          _tcp_socket(port),
          _epoll(timeout_sec * 1000)
    {
        // linsten_fe -> accepter
        add_connection(_tcp_socket.get_fd(), EPOLLET | EPOLLIN, std::bind(&reactor_server_t::accpeter, this));
        _tcp_socket.listen_fd(5);
    }
    bool run()
    {
        while (loop_once())
        {
            ;
        }
        return false;
    }
    bool loop_once()
    {
        int ready_num = _epoll.wait();
        if (ready_num < 0)
        {
            return false;
        }
        for (int i = 0; i < ready_num; i++)
        {
            dispatcher(*_epoll.get_event(i));
        }
        return true;
    }
    // 事件派发
    bool dispatcher(const epoll_event &evt)
    {
        // event --> react --> callback
        auto iter = _connect_map.find(evt.data.fd);
        if (iter == _connect_map.end())
            return del_fd(evt.data.fd);
        if (evt.events & EPOLLHUP || evt.events & EPOLLERR)
        {
            return del_fd(evt.data.fd);
        }
        if (evt.events & EPOLLIN)
        {
            iter->second->read();
        }
        if (evt.events & EPOLLOUT)
        {
            iter->second->write();
        }
        if (evt.events & EPOLLERR)
        {
            iter->second->error();
        }
        return true;
    }
    bool modify_read_write(int fd, bool read, bool write)
    {
        uint32_t events = 0;
        events |= read ? EPOLLIN : 0;
        events |= write ? EPOLLOUT : 0;
        return _epoll.modify(fd, events);
    }

private:
    bool accpeter()
    {
        // ET accept
        while (true)
        {
            int new_fd = _tcp_socket.accept_fd();
            if (new_fd < 0)
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else
                    return false;
            }
            else
            {
                // 绑定普通IO事件的反应方法
                return add_connection(new_fd, EPOLLET | EPOLLIN,
                                      std::bind(&reactor_server_t::tcp_reader, this, std::placeholders::_1),
                                      std::bind(&reactor_server_t::tcp_writer, this, std::placeholders::_1),
                                      std::bind(&reactor_server_t::tcp_error, this, std::placeholders::_1));
            }
        }
        return true;
    }
    bool add_connection(int fd, uint32_t events, handler_t read_func = nullptr, handler_t write_func = nullptr, handler_t error_func = nullptr)
    {
        // ET?
        if (events & EPOLLET)
            util_t::set_non_block(fd);
        // add to epoll
        _epoll.add(fd, events);
        auto p_new_con = new connection_t(fd, this);
        if (p_new_con == nullptr)
            return false;
        _connect_map[fd] = p_new_con;
        // set callback func
        if (read_func)
            p_new_con->set_read_handler(read_func);
        if (write_func)
            p_new_con->set_write_handler(write_func);
        if (error_func)
            p_new_con->set_error_handler(error_func);

        return true;
    }
    // 普通IO事件的反应方法
    bool tcp_reader(connection_t *con)
    {
        char tcp_buffer[1025];
        bzero(tcp_buffer, sizeof tcp_buffer);
        // ET: not stopping coping untill there's no data to read
        while (true)
        {
            ssize_t read_size = read(con->_fd, tcp_buffer, sizeof(tcp_buffer) - 1);
            if (read_size > 0)
            {
                con->_read_buffer += tcp_buffer;
            }
            else if (read_size == 0)
            {
                return this->del_fd(con->_fd);
            }
            else
            {
                if (errno == EINTR)
                    continue;
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else
                {
                    return tcp_error(con);
                }
            }
        }
        package_split(con->_read_buffer, con->_packages);
        if (!_req_handler(con))
            std::cerr << "req_handler error" << std::endl;
        con->_packages.clear();
        return true;
    }
    bool tcp_writer(connection_t *con)
    {
        // ET: 对于读事件关系交给上层管理
        //  业务处理完毕后直接调用此方法发送 若没发完才交给reactor
        // 发送完毕后再关闭对可写事件的关心
        while (true)
        {
            ssize_t writen_size = write(con->get_fd(), con->_write_buffer.c_str(), con->_write_buffer.size());
            if (writen_size > 0)
            {
                con->_write_buffer.erase(0, writen_size);
            }
            else if (writen_size == 0)
            {
                break;
            }
            else
            {
                // interrupted
                if (errno == EINTR)
                    continue;
                // buffer is filled
                else if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else
                    return tcp_error(con);
            }
        }
        if (con->is_write_buffer_empty())
            con->_p_back->modify_read_write(con->_fd, true, false);
        return true;
    }
    bool tcp_error(connection_t *con)
    {
        return del_fd(con->_fd);
    }
    bool del_fd(int fd)
    {
        if (_connect_map.find(fd) == _connect_map.end())
            return false;
        delete _connect_map[fd];
        _epoll.del(fd);
        _connect_map.erase(fd);
        close(fd);
        return true;
    }

private:
    // 网络
    tcp_socket_t _tcp_socket;
    // 多路转接
    epoll_t _epoll;
    // 事件
    std::unordered_map<int, connection_t *> _connect_map;
    // 业务启动方法
    handler_t _req_handler;
};
